/*
  +----------------------------------------------------------------------+
  | PHP Version 7                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2018 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/

/* $Id$ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_scarecrow.h"
/* If you declare any globals in php_scarecrow.h uncomment this:
ZEND_DECLARE_MODULE_GLOBALS(scarecrow)
*/

/* True global resources - no need for thread safety here */


/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("scarecrow.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_scarecrow_globals, scarecrow_globals)
    STD_PHP_INI_ENTRY("scarecrow.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_scarecrow_globals, scarecrow_globals)
PHP_INI_END()
*/
/* }}} */

PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("scarecrow.age", "42", PHP_INI_ALL, OnUpdateLong, age, zend_scarecrow_globals, scarecrow_globals)
STD_PHP_INI_ENTRY("scarecrow.name", "pengqingsong", PHP_INI_ALL, OnUpdateString, name, zend_scarecrow_globals, scarecrow_globals)
PHP_INI_END()


/* Remove the following function when you have successfully modified config.m4
   so that your module can be compiled into PHP, it exists only for testing
   purposes. */
/*
输出zavl变量
*/
void printZval(zval z)
{
	switch (Z_TYPE(z))
	{
	case IS_NULL:
		php_printf("NULL");
		break;
	case IS_LONG:
		php_printf("%f", Z_LVAL(z));
		break;
	case IS_DOUBLE:
		php_printf("%f",Z_DVAL(z));
		break;
	case IS_STRING:
		php_printf("str:%s len:%d", Z_STRVAL(z), Z_STRLEN(z));
		break;
	case IS_ARRAY:
		php_printf("Array");
		break;
	case IS_OBJECT:
		php_printf("Object");
		break;
	default:
		php_printf("Error type!");
		break;
	}
}

/*
打印日志信息
*/
static inline void trace(const char *file, int line, const char* function, const char *fmt, ...) {
	fprintf(stderr, "%s(%s:%d) - ", function, file, line);
	va_list args;
	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	fprintf(stderr, "\n");
	va_end(args);
}

/* 自定义函数参数变量列表定义区 */
/* 转换字符串为小写的参数列表 */
ZEND_BEGIN_ARG_INFO_EX(scarecrow_strtolower_args, 0, 0, 1)
ZEND_ARG_INFO(0, str)
ZEND_END_ARG_INFO()

/* 转换字符串为大写的参数列表 */
ZEND_BEGIN_ARG_INFO_EX(scarecrow_strtoupper_args, 0, 0, 1)
ZEND_ARG_INFO(0, str)
ZEND_END_ARG_INFO()

/* 自定义函数实现区 */
PHP_FUNCTION(confirm_scarecrow_compiled) {
	//zval b;
	//ZVAL_BOOL(&b, 1);
	//返回值的真实表达是将对象赋值到*return_value 上面，他是一个指针，外部将这个指针的值传递出去当成函数的返回值，也有宏的快捷方式来返回 ，但是解析后仍然是*return_value=xxx的形式，所以用那种看你自己(所有的宏定义返回列表)
	/*
	#define RETURN_BOOL(b) 					{ RETVAL_BOOL(b); return; }
	#define RETURN_NULL() 					{ RETVAL_NULL(); return;}
	#define RETURN_LONG(l) 					{ RETVAL_LONG(l); return; }
	#define RETURN_DOUBLE(d) 				{ RETVAL_DOUBLE(d); return; }
	#define RETURN_STR(s) 					{ RETVAL_STR(s); return; }
	#define RETURN_INTERNED_STR(s)			{ RETVAL_INTERNED_STR(s); return; }
	#define RETURN_NEW_STR(s)				{ RETVAL_NEW_STR(s); return; }
	#define RETURN_STR_COPY(s)				{ RETVAL_STR_COPY(s); return; }
	#define RETURN_STRING(s) 				{ RETVAL_STRING(s); return; }
	#define RETURN_STRINGL(s, l) 			{ RETVAL_STRINGL(s, l); return; }
	#define RETURN_EMPTY_STRING() 			{ RETVAL_EMPTY_STRING(); return; }
	#define RETURN_RES(r) 					{ RETVAL_RES(r); return; }
	#define RETURN_ARR(r) 					{ RETVAL_ARR(r); return; }
	#define RETURN_EMPTY_ARRAY()			{ RETVAL_EMPTY_ARRAY(); return; }
	#define RETURN_OBJ(r) 					{ RETVAL_OBJ(r); return; }
	#define RETURN_ZVAL(zv, copy, dtor)		{ RETVAL_ZVAL(zv, copy, dtor); return; }
	#define RETURN_FALSE  					{ RETVAL_FALSE; return; }
	#define RETURN_TRUE						{ RETVAL_TRUE; return; }
	*/
	//*return_value = b;
	//RETURN_BOOL(1);
	
	RETURN_BOOL(1);
	return ;
}

/* 转换字符串为小写 */
PHP_FUNCTION(scarecrow_strtolower) {
	TRACE("scarecrow_strtolower start");
	int num_args = ZEND_CALL_NUM_ARGS(execute_data);
	TRACE(num_args);
	zval *args = ZEND_CALL_ARG(execute_data, 1);
	zval_get_string(args);
	zval str_zval;
	zend_string *str = zend_string_init("IS_STRING", sizeof("IS_STRING") - 1, 0);
	ZVAL_STR(&str_zval, str);
	*return_value = str_zval;

	//int num_args = ZEND_CALL_NUM_ARGS(execute_data);
	//zval *args = ZEND_CALL_ARG(execute_data, 1);
	char *arg = NULL;
	size_t arg_len, len;
	
	//获取函数传入的参数 第一个参数代表有几个参数，第二个表示获取数据格式 b:Boolean布尔型;l:Integer整形;d:float浮点型;s:string字符串;r:resource资源;a:array数组型;o:object instance对象；OObject instance of a specified type 特定类型的对象;z:任意类型;Z:zval**类型;f:表示函数、方法名称
	//其中各种类型对应的载体:b:zend_bool;l:long;d:double;s:char *,int 前者接受数据后者接受长度;r:zval *;a:zval *;o:zval *;O:zval *,zend_class_entry *;z:zval *;Z:zavl **;
	//注意，所有的PHP语言中的复合类型参数都需要zval*类型来作为载体，因为它们都是内核自定义的一些数据结构。我们一定要确认参数和载体的类型一直，如果需要，它可以进行类型转换，比如把array转换成stdClass对象。如果传递给函数的参数数量小于zend_parse_parameters()要接收的参数数量，它便会执行失败，并返回FAILURE。如果我们需要接收多个参数，可以直接在zend_parse_paramenters()的参数里罗列接收载体便可以了;
	//除了上面定义的参数，还有其它的三个参数来增强我们接受参数的能力，如下：
	/*
	Type Modifier	Meaning
		|		它之前的参数都是必须的，之后的都是非必须的，也就是有默认值的。
		!		如果接收了一个PHP语言里的null变量，则直接把其转成C语言里的NULL，而不是封装成IS_NULL类型的zval。
		/		如果传递过来的变量与别的变量共用一个zval，而且不是引用，则进行强制分离，新的zval的is_ref__gc==0, and refcount__gc==1.
	*/
	/*
	如果你想让你的扩展能够兼容老版本的PHP，或者你只想以zval*为载体来接收参数，便可以考虑使用zend_get_parameters()函数来接收参数。zend_get_parameters()与zend_parse_parameters()不同，从名字上我们便可以看出，它直接获取，而不做解析。首先，它不会自动进行类型转换，所有的参数在扩展实现中的载体都需要是zval*类型的，下面让我们来看一个最简单的例子：
		ZEND_FUNCTION(sample_onearg)
			{
				zval *firstarg;
				if (zend_get_parameters(ZEND_NUM_ARGS(), 1, &firstarg)== FAILURE)
				{
					php_error_docref(NULL TSRMLS_CC, E_WARNING,"Expected at least 1 parameter.");
					RETURN_NULL();
				}
			}
	*/
	/*
	有两种其它的zend_get_parameter_**函数，专门用来解决参数很多或者无法提前知道参数数目的问题。想一下php语言中var_dump()函数的用法，我们可以向其传递任意数量的参数，它在内核中的实现其实是这样的：
	ZEND_FUNCTION(var_dump)
		{
			int i, argc = ZEND_NUM_ARGS();
			zval ***args;

			args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0);
			if (ZEND_NUM_ARGS() == 0 || zend_get_parameters_array_ex(argc, args) == FAILURE)
			{
				efree(args);
				WRONG_PARAM_COUNT;
			}
			for (i=0; i < argc; i++)
			{
				php_var_dump(args[i], 1 TSRMLS_CC);
			}
			efree(args);
		}
	*/
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) {
		return;
	}

	char *temp = (char *)malloc(arg_len * 2 + 1);
	memcpy(temp, arg, arg_len);
	memcpy(temp+arg_len, arg, arg_len);
	*(temp + 2 * arg_len) = '\0';
	zval test;
	zend_string *testStr = zend_string_init(temp, arg_len * 2 + 1, 0);
	ZVAL_STR(&test, testStr);
	*return_value = test;
	free(temp);
	//zend_parse_parameters(ZEND_NUM_ARGS());
	TRACE("scarecrow_strtolower end");
}

/* zend变量zval的各种类型的使用 */
PHP_FUNCTION(scarecrow_zval_use) {
	//TRACE("zif_testzval");

	// undef
	zval undef_zval;
	ZVAL_UNDEF(&undef_zval);
	assert(Z_TYPE(undef_zval) == IS_UNDEF);

	// null
	zval null_zval;
	ZVAL_NULL(&null_zval);
	assert(Z_TYPE(null_zval) == IS_NULL);

	// bool
	zval bool_zval;
	ZVAL_BOOL(&bool_zval, 1);
	assert(Z_TYPE(bool_zval) == IS_TRUE);

	// long
	zval long_zval;
	ZVAL_LONG(&long_zval, 1024);
	assert(Z_TYPE(long_zval) == IS_LONG);
	assert(Z_LVAL_P(&long_zval) == 1024);

	// string
	zval str_zval;
	zend_string *str = zend_string_init("IS_STRING", sizeof("IS_STRING") - 1, 0);
	ZVAL_STR(&str_zval, str);
	assert(Z_TYPE(str_zval) == IS_STRING);
	assert(strncmp(Z_STRVAL_P(&str_zval), "IS_STRING", Z_STRLEN_P(&str_zval)) == 0);
	zval_addref_p(&str_zval); // add 1 ref for arr_zval below
	zval_addref_p(&str_zval); // add 1 ref for ref_zval below
	zval_ptr_dtor(&str_zval);


	// array
	zval arr_zval;
	zend_array *arr = emalloc(sizeof(*arr));
	zend_hash_init(arr, 0, NULL, ZVAL_PTR_DTOR, 0);

	ZVAL_ARR(&arr_zval, arr);
	zend_symtable_str_update(arr, "str_zval", sizeof("str_zval") - 1, &str_zval);
	zend_symtable_str_update(arr, "str_long", sizeof("str_long") - 1, &long_zval);
	zval *zval_in_arr = zend_symtable_str_find(arr, "str_zval", sizeof("str_zval") - 1);
	zval *zval_in_arr1 = zend_symtable_str_find(arr, "str_long", sizeof("str_long") - 1);
	uint32_t num_elems = zend_hash_num_elements(Z_ARRVAL_P(&arr_zval));
	zval_ptr_dtor(&arr_zval);
	
	// reference
	zval ref_zval;
	zend_reference *ref = emalloc(sizeof(*ref));
	GC_TYPE_INFO(ref) = IS_REFERENCE;
	memcpy(&(ref->val), &str_zval, sizeof(str_zval));
	ZVAL_REF(&ref_zval, ref);
	assert(Z_TYPE(ref_zval) == IS_REFERENCE);
	zval_addref_p(&ref_zval); // add 1 ref for ref_ref_zval
	zval_ptr_dtor(&ref_zval);

	// zval ref to reference
	zval ref_ref_zval;
	memcpy(&ref_ref_zval, &ref_zval, sizeof(ref_zval));
	zval *real_zval = Z_REFVAL_P(&ref_ref_zval);
	zend_string *real_str = Z_STR_P(real_zval);
	assert(real_str == str);
	zend_string_release(real_str);
	ZVAL_STR(real_zval, zend_string_init("IS_STRING_TOO", sizeof("IS_STRING_TOO") - 1, 0));
	zval_ptr_dtor(&ref_ref_zval);

	// tips:
	// zval_addref_p/zval_delref_p can only be applied to zval with ref count
	zval double_zval;
	ZVAL_DOUBLE(&double_zval, 13.14);
	assert(!Z_REFCOUNTED_P(&double_zval));
	// the following line will trigger assert-coredump
	// zval_delref_p(&double_zval);

	// zval copy
	zval copy_from;
	ZVAL_STR(&copy_from, zend_string_init("test ZVAL_COPY", sizeof("test ZVAL_COPY") - 1, 0));

	zval copy_to1;
	ZVAL_COPY(&copy_to1, &copy_from);

	// same as ZVAL_COPY above
	zval copy_to2;
	memcpy(&copy_to2, &copy_from, sizeof(copy_from)); // same as ZVAL_COPY_VALUE
	if (Z_REFCOUNTED_P(&copy_from)) {
		zval_addref_p(&copy_to2);
	}

	// release all reference count
	zval_ptr_dtor(&copy_from);
	zval_ptr_dtor(&copy_to1);
	zval_ptr_dtor(&copy_to2);
}

/* 转换字符串为大写 */
PHP_FUNCTION(scarecrow_strtoupper) {

}

/* Every user-visible function in PHP should document itself in the source */
/* {{{ proto string confirm_scarecrow_compiled(string arg)
   Return a string to confirm that the module is compiled in */


/* }}} */
/* The previous line is meant for vim and emacs, so it can correctly fold and
   unfold functions in source code. See the corresponding marks just before
   function definition, where the functions purpose is also documented. Please
   follow this convention for the convenience of others editing your code.
*/



/* {{{ php_scarecrow_init_globals
 */
/* Uncomment this function if you have INI entries
static void php_scarecrow_init_globals(zend_scarecrow_globals *scarecrow_globals)
{
	scarecrow_globals->global_value = 0;
	scarecrow_globals->global_string = NULL;
}
*/
/* }}} */

/*
{{{ PHP_MINIT_FUNCTION
此函数为生命周期函数，当模块加载的时候被调用
*/

PHP_MINIT_FUNCTION(scarecrow)
{
	TRACE("this is minit function!");
	/* If you have INI entries, uncomment these lines
	REGISTER_INI_ENTRIES();
	*/
	//注册加载全局php.ini配置文件
	REGISTER_INI_ENTRIES();

	//注册全局常量
	REGISTER_STRING_CONSTANT("SCARECROW_VERSION","1.0", CONST_CS | CONST_PERSISTENT);
	return SUCCESS;
}
/* }}} */



/* {{{ PHP_MSHUTDOWN_FUNCTION
此函数为生命周期函数，当模块被卸载的时候调用
*/
PHP_MSHUTDOWN_FUNCTION(scarecrow)
{
	TRACE("this is mshutdown function!");
	/* uncomment this line if you have INI entries
	UNREGISTER_INI_ENTRIES();
	*/
	//销毁配置文件的缓存
	UNREGISTER_INI_ENTRIES();
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
此函数为生命周期函数，当FAST/CGI或者CGI调用的时候触发回调
 */
PHP_RINIT_FUNCTION(scarecrow)
{
	TRACE("this is rinit function!");
#if defined(COMPILE_DL_SCARECROW) && defined(ZTS)
	ZEND_TSRMLS_CACHE_UPDATE();
#endif

	//根据数据类型调用zend_ini_##type 来实现获取Ini配置信息
	zend_long value = zend_ini_long("scarecrow.age", 13, 0);
	//char *extName = zend_ini_string("scarecrow.name", 14, 0);
	char *extName1 = zend_ini_string("scarecrow.name", 14, 1);
	//TRACE("ini: %s=%s", "scarecrow.name", extName);
	TRACE("ini: %s=%s", "scarecrow.name1", extName1);
	TRACE("ini: %s=%d", "scarecrow.age", value);
	return SUCCESS;
}
/* }}} */

/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
此函数为生命周期函数，当FAST/CGI或者CGI调用完成卸载的时候触发回调
 */
PHP_RSHUTDOWN_FUNCTION(scarecrow)
{
	TRACE("this is rshutdown function!");
	return SUCCESS;
}
/* }}} */

/**
{{{ PHP_MINFO_FUNCTION
此函数是用于当调用phpinfo的时候回调的函数
*/
PHP_MINFO_FUNCTION(scarecrow)
{
	TRACE("this is minfo function!");

	/*
	zend_long age = 0;
	char sex[5];
	sprintf(sex, "%s", SCARECROW_G(sex) == 1 ? "boy" : "girl");
	sprintf(age, "%d", SCARECROW_G(age));
	/* 声明在phpinfo行数调用时显示的table信息 */
	/**
	php_info_print_table_start();
	php_info_print_table_header(2, "scarecrow list", "enabled");
	php_info_print_table_row(2, "Scarecrow.Version", PHP_SCARECROW_VERSION);
	php_info_print_table_row(2, "Scarecrow.is.name", SCARECROW_G(name));
	php_info_print_table_row(2, "Scarecrow.is.ShuaiGe", SCARECROW_G(isshuaige));
	//php_info_print_table_row(2, "Scarecrow.is.age", age);
	php_info_print_table_row(2, "Scarecrow.is.sex", sex);
	php_info_print_table_row(2, "Scarecrow.is.address", SCARECROW_G(address));
	php_printf("<tr>"
		"<td class=\"v\">Scarecrow</td>"
		"<td class=\"v\">"
		"<a href=\"http://127.0.0.1\""
		" alt=\"Scarecrow Love You\">"
		"<img src=\"http://127.0.0.1/web/tx.png\" />"
		"</a></td></tr>");
	php_info_print_table_end();
	*/

	php_info_print_table_start();
	php_info_print_table_header(2, "scarecrow params", "enabled");
	php_info_print_table_header(2, "scarecrow.author", "Scarecrow");
	php_info_print_table_header(2, "scarecrow.address","ChongQing");
	php_info_print_table_end();

	/* Remove comments if you have entries in php.ini
	DISPLAY_INI_ENTRIES();
	*/
}
/* }}} */

/* {{{ scarecrow_functions[]
 *
 * Every user visible function must have an entry in scarecrow_functions[].
 * 注册全局函数列表，必须以PHP_EF_END(即为空)结束
 */
const zend_function_entry scarecrow_functions[] = {
	PHP_FE(confirm_scarecrow_compiled, NULL)
	PHP_FE(scarecrow_strtolower, scarecrow_strtolower_args)
	PHP_FE(scarecrow_strtoupper, scarecrow_strtoupper_args)
	PHP_FE(scarecrow_zval_use, NULL)
	PHP_FE_END	/* Must be the last line in scarecrow_functions[] */
};
/* }}} */

/* {{{ scarecrow_module_entry
 */

/*这里是添加该扩展依赖的外部扩展，如果在运行时PHP没有找到相关的依赖扩展则此扩展不会被加载*/
zend_module_dep deps[] = {
		{"standard", NULL, NULL, MODULE_DEP_REQUIRED},
		{ NULL, NULL, NULL, 0},
};

/**
此变量为对外的像zend引擎注册此扩展的信息
*/

//zend_module_entry scarecrow_module_entry = {
//	STANDARD_MODULE_HEADER_EX,/*基础变量，但是一般不随意改动，系统默认*/
//	NULL, /*配置文件初始化入口*/
//	deps, /*依赖的三方PHP扩展列表*/
//	"scarecrow", /*扩展名称*/
//	scarecrow_functions,/*函数列表*/
//	PHP_MINIT(scarecrow),/*扩展初始化回调函数*/
//	PHP_MSHUTDOWN(scarecrow),/*扩展卸载时回调函数*/
//	PHP_RINIT(scarecrow),		/*当请求开始处理时回调 Replace with NULL if there's nothing to do at request start */
//	PHP_RSHUTDOWN(scarecrow),	/*当请求处理结束后回调 Replace with NULL if there's nothing to do at request end */
//	PHP_MINFO(scarecrow),/*当调用phpinfo的时候回调函数*/
//	PHP_SCARECROW_VERSION, /*当前扩展的版本号*/
//	STANDARD_MODULE_PROPERTIES
//};

zend_module_entry scarecrow_module_entry = {
	STANDARD_MODULE_HEADER_EX,
	NULL,
	deps,
	"scarecrow",
	scarecrow_functions,
	PHP_MINIT(scarecrow),
	PHP_MSHUTDOWN(scarecrow),
	PHP_RINIT(scarecrow),
	PHP_RSHUTDOWN(scarecrow),
	PHP_MINFO(scarecrow),
	PHP_SCARECROW_VERSION,
	STANDARD_MODULE_PROPERTIES
};

/* }}} */

#ifdef COMPILE_DL_SCARECROW
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(scarecrow) /* 返回当前扩展的注册内核信息 */
#endif

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
